﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Xml;
using System.Configuration;

namespace QSTOOL.Codes
{
    public class XMLTool
    {
        public string path { get; private set; }
        public ArrayList allcommands { get; private set; }
        public bool ready { get; private set; }
        private XmlDocument xmlDoc { get; set; }
        private XmlNode root { get; set; }
        private XmlNodeList steps { get; set; }
        public const string OPERATION = "OPERATION";
        public const string STEP = "STEP";

        public XMLTool()
        {
            string strTemp = ConfigurationManager.AppSettings["COMMAND-LIST"].ToString();
            this.path = strTemp;
            if (strTemp == "" || strTemp == null)
            {
                this.ready = false;
            }
            else
            {
                Init();
                this.ready = true;
            }
        }

        private void Init()
        {
            this.allcommands = new ArrayList();
            this.xmlDoc = new XmlDocument();
            xmlDoc.Load(this.path);
            this.root = this.xmlDoc.SelectSingleNode("HTZQ");
            this.steps = root.ChildNodes;
            LoadCommands();
        }

        private void LoadCommands()
        {
            int i = 0;
            //this.allcommands.Clear();
            
            foreach (XmlNode nodeStep in this.steps)
            {
                List<Command> commands = new List<Command>();
                
                foreach (XmlNode nodeCMD in nodeStep.ChildNodes)
                {
                    Command cmd = new Command(nodeCMD);
                    commands.Add(cmd);                                
                }
                
                this.allcommands.Add(commands);
                i++;
            }
        }

        private void ReloadXml()
        {
            this.allcommands.Clear();
            xmlDoc.Load(this.path);
            this.root = this.xmlDoc.SelectSingleNode("HTZQ");
            this.steps = root.ChildNodes;
            LoadCommands();
        }

        ///// <summary>
        ///// Find node by GUID
        ///// </summary>
        ///// <param name="GUID"></param>
        ///// <returns></returns>
        //private XmlNode FindNode(string GUID)
        //{
        //    XmlNode node = null;

        //    foreach (XmlNode nodeStep in steps)
        //    {
        //        node = nodeStep.SelectSingleNode("OPERATION[@GUID='" + GUID + "']");
        //        if (node != null) 
        //        {
        //            break;
        //        }
        //    }

        //    return node;
        //}

        /// <summary>
        /// Find node by GUID in CommandList
        /// </summary>
        /// <param name="GUID">GUID to Find</param>
        /// <returns>ArrayList, the 1st element is a command list, the 2nd is the position</returns>
        private ArrayList FindNode(string GUID)
        {
            ArrayList aryResult = new ArrayList();
            List<Command> lstCommand = null;
            int position = -1;
            int i = 0;

            foreach (List<Command> list in this.allcommands)
            {
                foreach (Command cmd in list)
                {
                    if (cmd.guid == GUID)
                    {
                        lstCommand = list;
                        position = i;
                        break;
                    }
                    i++;
                }

                if (lstCommand != null)
                {
                    break;
                }

                i = 0;
            }

            aryResult.Add(lstCommand);
            aryResult.Add(position);

            return aryResult;
        }

        private XmlElement CommandToElement(Command command)
        {
            XmlElement element = xmlDoc.CreateElement(OPERATION);
            element.SetAttribute("COMMAND", command.command);
            element.SetAttribute("DETAIL", command.detail);
            element.SetAttribute("SOURCE", command.source);
            element.SetAttribute("TARGET", command.target);
            element.SetAttribute("DESCRIPTION", command.description);
            element.SetAttribute("GUID", Guid.NewGuid().ToString());
            return element;
        }

        /// <summary>
        /// Insert xml nodes into the specific position
        /// </summary>
        /// <param name="command">Command to insert</param>
        /// <param name="GUID">The GUID of the reference node</param>
        /// <param name="type">0:Append; 1:Insert After; 2:Insert Before</param>
        public void Insert(Command command, string ref_GUID, int type)
        {
            //XmlNode ref_node = FindNode(GUID);
            //XmlElement element = CommandToElement(command);
            //switch (type)
            //{
            //    case Operation.APPEND:
            //        ref_node.ParentNode.AppendChild(element);
            //        break;
            //    case Operation.INSERT_AFTER:
            //        ref_node.ParentNode.InsertAfter(element, ref_node);
            //        break;
            //    case Operation.INSERT_BEFORE:
            //        ref_node.ParentNode.InsertBefore(element, ref_node);
            //        break;
            //}

            //Save();
            ArrayList aryRef = FindNode(ref_GUID);
            List<Command> list = (List<Command>)aryRef[0];
            int position = (int)aryRef[1];

            switch (type)
            {
                case Operation.APPEND:
                    list.Add(command);
                    break;
                case Operation.INSERT_AFTER:
                    list.Insert(position + 1, command);
                    break;
                case Operation.INSERT_BEFORE:
                    list.Insert(position, command);
                    break;
            }
        }

        /// <summary>
        /// Insert xml node at the end
        /// </summary>
        /// <param name="node">Node to insert</param>
        /// <param name="step">In which step</param>
        public void Insert(XmlNode node, int step)
        {
            this.steps[step].AppendChild(node);
        }

        /// <summary>
        /// Insert xml node at the end
        /// </summary>
        /// <param name="command">Command to insert</param>
        /// <param name="step">In which step</param>
        public void Insert(Command command, int step)
        {
            //XmlElement element = CommandToElement(command);
            //steps[step].AppendChild(element);
            //Save();

            if (this.allcommands.Count < step)
            {
                List<Command> newStep = new List<Command>();
                newStep.Add(command);
                this.allcommands.Add(newStep);
            }
            else 
            {
                ((List<Command>)this.allcommands[step]).Add(command);
            }
        }


        /// <summary>
        /// Delete specific node
        /// </summary>
        /// <param name="node">Node to delete</param>
        public void Delete(XmlNode node)
        {
            node.ParentNode.RemoveChild(node);
            Save();
        }

        /// <summary>
        /// Delete specific node
        /// </summary>
        /// <param name="GUID">The GUID of the node to delete</param>
        public void Delete(string GUID)
        {
            //XmlNode node = null;
            //bool bFlag = false;

            //foreach (XmlNode nodeStep in this.steps)
            //{
            //    foreach (XmlNode nodeCommand in nodeStep.ChildNodes)
            //    {
            //        if (nodeCommand.Attributes["GUID"].Value == GUID)
            //        {
            //            node = nodeCommand;
            //            bFlag = true;
            //            break;
            //        }
            //    }

            //    if (bFlag)
            //    {
            //        break;
            //    }
            //}

            //if (node != null)
            //{
            //    node.ParentNode.RemoveChild(node);
            //}
            //Save();
            ArrayList aryRef = FindNode(GUID);
            List<Command> list = (List<Command>)aryRef[0];
            int position = (int)aryRef[1];

            list.RemoveAt(position);
        }

        /// <summary>
        /// Modify node
        /// </summary>
        /// <param name="node">New node</param>
        /// <param name="original_node">Original node</param>
        public void Modify(XmlNode node, XmlNode original_node)
        { 
        
        }

        /// <summary>
        /// Modify node
        /// </summary>
        /// <param name="cmd">Node to Modify</param>
        /// <param name="GUID">The GUID of the node</param>
        public void Modify(Command cmd)
        {
            ArrayList aryRef = FindNode(cmd.guid);
            List<Command> list = (List<Command>)aryRef[0];
            int position = (int)aryRef[1];

            list[position].command = cmd.command;
            list[position].detail = cmd.detail;
            list[position].source = cmd.source;
            list[position].target = cmd.target;
            list[position].description = cmd.description;
            list[position].guid = cmd.guid;
        }

        public void Save()
        {
            //Rewrite the xml file refer to the ArrayList allCommand
            this.root.RemoveAll();
            int i = 1;

            foreach (List<Command> lstStep in allcommands)
            {
                XmlElement elementStep = xmlDoc.CreateElement(STEP);
                elementStep.SetAttribute(Attribute.ID, i.ToString());

                foreach (Command cmd in lstStep)
                {
                    XmlElement elementOpreation = xmlDoc.CreateElement(OPERATION);
                    elementOpreation.SetAttribute(Attribute.COMMAND, cmd.command);
                    elementOpreation.SetAttribute(Attribute.DETAIL, cmd.detail);
                    elementOpreation.SetAttribute(Attribute.SOURCE, cmd.source);
                    elementOpreation.SetAttribute(Attribute.TARGET, cmd.target);
                    elementOpreation.SetAttribute(Attribute.DESCRIPTION, cmd.description);
                    elementOpreation.SetAttribute(Attribute.GUID, cmd.guid);

                    elementStep.AppendChild(elementOpreation);
                }
                
                this.root.AppendChild(elementStep);
                i++;
            }
            this.xmlDoc.Save(this.path);
        }

    }
}
